#include "udatetimeedit.h"
#include "ucalendarwidget.h"

#include <QPainter>
#include <QPaintEvent>
#include <QPainterPath>
#include <QLineEdit>
#include <QGuiApplication>
#include <QScreen>
#include <QCalendarWidget>
#include <QIcon>
#include <QStyleOption>

class UDateTimeEditPrivate
{
public:
    UDateTimeEditPrivate(UDateTimeEdit* q) : q_ptr(q) {}
    ~UDateTimeEditPrivate() {}

    void init();

    void showCalendarPopup();

    void syncCalendarDate();

private:
    Q_DECLARE_PUBLIC(UDateTimeEdit)
    Q_DISABLE_COPY(UDateTimeEditPrivate)
    UDateTimeEdit* const q_ptr = nullptr;

    int radius = 8;
    double borderWidth = 1.5;
    QMetaType::Type parserType = QMetaType::QDateTime;
    bool iconVisible = true;
    bool isClicked = false;
};

void UDateTimeEditPrivate::init()
{
    Q_Q(UDateTimeEdit);
    q->setFrame(false);
    q->setButtonSymbols(QDateTimeEdit::NoButtons);
    if (q->lineEdit())
    {
        q->lineEdit()->installEventFilter(q);
        q->lineEdit()->setReadOnly(true);
    }
    q->setDisplayFormat("yyyy-MM-dd hh:mm:ss");
}

void UDateTimeEditPrivate::showCalendarPopup()
{
    Q_Q(QDateTimeEdit);

    QWidget* monthCalendar = q->findChild<QWidget*>("qt_datetimedit_calendar");
    if (nullptr == monthCalendar)
    {
        return;
    }
    if (q->calendarWidget())
    {
        q->disconnect(q->calendarWidget(), &QCalendarWidget::clicked, nullptr, nullptr);
    }
    if (monthCalendar->isVisible())
    {
        return;
    }
    QPoint pos = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().bottomRight() : q->rect().bottomLeft();
    pos.setY(pos.y() + 2);
    QPoint pos2 = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().topRight() : q->rect().topLeft();
    pos2.setY(pos2.y() - 2);
    pos = q->mapToGlobal(pos);
    pos2 = q->mapToGlobal(pos2);
    QSize size = monthCalendar->sizeHint();
    QRect screen = QGuiApplication::screenAt(pos)->availableGeometry();

    if (q->layoutDirection() == Qt::RightToLeft)
    {
        pos.setX(pos.x() - size.width());
        pos2.setX(pos2.x() - size.width());
        if (pos.x() < screen.left())
            pos.setX(qMax(pos.x(), screen.left()));
        else if (pos.x() + size.width() > screen.right())
            pos.setX(qMax(pos.x() - size.width(), screen.right() - size.width()));
    }
    else
    {
        if (pos.x() + size.width() > screen.right())
            pos.setX(screen.right() - size.width());

        pos.setX(qMax(pos.x(), screen.left()));
    }
    if (pos.y() + size.height() > screen.bottom())
        pos.setY(pos2.y() - size.height());
    else if (pos.y() < screen.top())
        pos.setY(screen.top());

    if (pos.y() < screen.top())
        pos.setY(screen.top());

    if (pos.y() + size.height() > screen.bottom())
        pos.setY(screen.bottom() - size.height());

    isClicked = true;
    monthCalendar->move(pos);
    syncCalendarDate();
    monthCalendar->show();
}

void UDateTimeEditPrivate::syncCalendarDate()
{
    Q_Q(UDateTimeEdit);
    if (q->calendarWidget())
    {
        q->calendarWidget()->setDateRange(q->minimumDate(), q->maximumDate());
        q->calendarWidget()->setSelectedDate(q->date());
    }

    if (auto ucalendarWidget = qobject_cast<UCalendarWidget*>(q->calendarWidget()))
    {
        q->connect(ucalendarWidget, &UCalendarWidget::selectDateTime, q, &UDateTimeEdit::onSyncDateTimeEditDate, Qt::UniqueConnection);
        q->connect(ucalendarWidget, &UCalendarWidget::closed, q, [=]()
        {
            isClicked = false;
            q->update();
        }, Qt::UniqueConnection);
        ucalendarWidget->setTimeVisible(QMetaType::QDateTime == parserType);
    }
}

UDateTimeEdit::UDateTimeEdit(QWidget* parent) : QDateTimeEdit(parent)
    , d_ptr(new UDateTimeEditPrivate(this))
{
    Q_D(UDateTimeEdit);
    d->init();
    d->parserType = QMetaType::QDateTime;
}

UDateTimeEdit::UDateTimeEdit(const QVariant &val, QMetaType::Type parserType, QWidget* parent)
    : QDateTimeEdit(val, parserType, parent)
    , d_ptr(new UDateTimeEditPrivate(this))
{
    Q_D(UDateTimeEdit);
    d->init();
    d->parserType = parserType;
}

UDateTimeEdit::~UDateTimeEdit()
{
}

void UDateTimeEdit::setBorderWidth(double width)
{
    Q_D(UDateTimeEdit);
    if (d->borderWidth != width)
    {
        d->borderWidth = width;
        update();
    }
}

void UDateTimeEdit::setRadius(int radius)
{
    Q_D(UDateTimeEdit);
    if (d->radius != radius)
    {
        d->radius = radius;
        update();
    }
}

void UDateTimeEdit::setRightIconVisible(bool isVisible)
{
    Q_D(UDateTimeEdit);
    if (d->iconVisible != isVisible)
    {
        d->iconVisible = isVisible;
        update();
    }
}

void UDateTimeEdit::onSyncDateTimeEditDate(const QDateTime &time)
{
    setDateTime(time);
    QWidget* monthCalendar = findChild<QWidget*>("qt_datetimedit_calendar");
    if (monthCalendar)
    {
        monthCalendar->close();
    }
}

void UDateTimeEdit::paintEvent(QPaintEvent* event)
{
    Q_D(UDateTimeEdit);

    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    qreal opacity = isEnabled() ? 1.0 : 0.3;
    painter.setOpacity(opacity);

    QPainterPath rectPath;
    QRect rect = this->rect().adjusted(d->borderWidth, d->borderWidth, -d->borderWidth, -d->borderWidth);
    rectPath.addRoundedRect(rect, d->radius, d->radius);

    QStyleOption opt;
    opt.init(this);
    QPen pen = QPen(Qt::transparent, d->borderWidth);

    if (opt.state & QStyle::State_Enabled)
    {
        if (opt.state & QStyle::State_Selected)
        {
            pen = QPen(palette().color(QPalette::Highlight), d->borderWidth);
        }
        else if (opt.state & QStyle::State_MouseOver)
        {
            pen = QPen(palette().color(QPalette::Text), d->borderWidth);
        }
    }

    if (d->isClicked)
        pen = QPen(palette().color(QPalette::Highlight), d->borderWidth);

    painter.setBrush(palette().color(QPalette::Button));
    painter.setPen(pen);
    painter.drawPath(rectPath);

    QAbstractSpinBox::paintEvent(event);

    if (d->iconVisible)
    {
        painter.save();
        QSize iconSize = QSize(this->height() - 12, this->height() - 12);
        QRect iconRect = QRect(width() - 6 - iconSize.width(), 6, iconSize.width(), iconSize.height());
        QIcon icon = QIcon::fromTheme("utk_calendar");
        auto mode = isEnabled() ? QIcon::Normal : QIcon::Disabled;
        icon.paint(&painter, iconRect, Qt::AlignCenter, mode);
        painter.restore();
    }

    if (hasFocus() && d->borderWidth > 0)
    {
        painter.save();
        painter.setBrush(Qt::NoBrush);
        painter.setPen(QPen(palette().color(QPalette::Highlight), d->borderWidth));
        painter.drawPath(rectPath);
        painter.restore();
    }
}

void UDateTimeEdit::mousePressEvent(QMouseEvent* event)
{
    Q_D(UDateTimeEdit);
    event->accept();
    if (isReadOnly())
    {
        return;
    }
    d->showCalendarPopup();
}

bool UDateTimeEdit::eventFilter(QObject* watched, QEvent* event)
{
    Q_D(UDateTimeEdit);
    if (watched == lineEdit() && (QEvent::MouseButtonPress == event->type()))
    {
        if (!isReadOnly())
        {
            d->showCalendarPopup();
        }
        return true;
    }
    return QDateTimeEdit::eventFilter(watched, event);
}


UDateEdit::UDateEdit(QWidget* parent)
    : UDateTimeEdit(QDate(2000, 1, 1), QMetaType::QDate, parent)
{
    setDisplayFormat("yyyy-MM-dd");
    setDate(QDate::currentDate());
}

UDateEdit::UDateEdit(const QDate &date, QWidget* parent)
    : UDateTimeEdit(date, QMetaType::QDate, parent)
{
    setDisplayFormat("yyyy-MM-dd");
}

UDateEdit::~UDateEdit()
{
}

UTimeEdit::UTimeEdit(QWidget* parent)
    : UDateTimeEdit(QTime(0, 0, 0), QMetaType::QTime, parent)
{
    setDisplayFormat("hh:mm:ss");
    setRightIconVisible(false);
}

UTimeEdit::UTimeEdit(const QTime &time, QWidget* parent)
    : UDateTimeEdit(time, QMetaType::QTime, parent)
{
    setDisplayFormat("hh:mm:ss");
    setRightIconVisible(false);
}

UTimeEdit::~UTimeEdit()
{
}
